package com.mask.token.service;

import com.mask.token.config.MaskTokenProperties;
import com.mask.token.model.MaskUserDetails;
import com.mask.token.model.TokenInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Optional;

/**
 * Token登录策略服务
 * 实现不同的登录策略：账号互踢、多设备登录、同用户返回同一Token
 *
 * @author mask
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MaskTokenLoginStrategy {

    private final MaskTokenService tokenService;
    private final MaskTokenProperties properties;

    /**
     * 登录策略枚举
     */
    public enum LoginStrategy {
        /**
         * 单点登录 - 账号互踢
         */
        SINGLE_SIGN_ON,
        
        /**
         * 多设备登录
         */
        MULTI_DEVICE,
        
        /**
         * 同用户返回同一Token
         */
        REUSE_TOKEN
    }

    /**
     * 处理用户登录
     *
     * @param userDetails 用户详情
     * @param strategy 登录策略
     * @return Token字符串
     */
    public String handleLogin(MaskUserDetails userDetails, LoginStrategy strategy) {
        Long userId = userDetails.getUserId();
        String deviceId = userDetails.getDeviceId();

        switch (strategy) {
            case SINGLE_SIGN_ON:
                return handleSingleSignOn(userDetails);
            case MULTI_DEVICE:
                return handleMultiDeviceLogin(userDetails);
            case REUSE_TOKEN:
                return handleReuseToken(userDetails);
            default:
                return handleDefaultLogin(userDetails);
        }
    }

    /**
     * 单点登录处理（账号互踢）
     * 新登录会踢出该用户的所有旧Token
     *
     * @param userDetails 用户详情
     * @return Token字符串
     */
    private String handleSingleSignOn(MaskUserDetails userDetails) {
        Long userId = userDetails.getUserId();
        
        log.info("单点登录模式 - 用户: {}, 设备: {}", userDetails.getUsername(), userDetails.getDeviceId());
        
        // 1. 踢出该用户的所有旧Token
        kickOutUserAllTokens(userId, "新登录踢出旧Token");
        
        // 2. 生成新Token
        String newToken = tokenService.generateAccessToken(userDetails);
        
        log.info("用户 {} 单点登录成功，已踢出所有旧Token", userDetails.getUsername());
        return newToken;
    }

    /**
     * 多设备登录处理
     * 允许用户在多设备上同时登录
     *
     * @param userDetails 用户详情
     * @return Token字符串
     */
    private String handleMultiDeviceLogin(MaskUserDetails userDetails) {
        Long userId = userDetails.getUserId();
        String deviceId = userDetails.getDeviceId();
        
        log.info("多设备登录模式 - 用户: {}, 设备: {}", userDetails.getUsername(), deviceId);
        
        // 1. 检查是否超过最大设备数限制
        int currentDeviceCount = tokenService.getOnlineDeviceCount(userId);
        int maxSessions = properties.getMaxSessions();
        
        if (maxSessions > 0 && currentDeviceCount >= maxSessions) {
            // 踢出最旧的设备
            kickOutOldestDevice(userId);
            log.info("设备数量超限，已踢出最旧设备 - 用户: {}, 当前设备数: {}", 
                    userDetails.getUsername(), currentDeviceCount);
        }
        
        // 2. 检查同一设备是否已有Token
        if (deviceId != null) {
            Optional<TokenInfo> existingToken = findTokenByUserAndDevice(userId, deviceId);
            if (existingToken.isPresent() && !existingToken.get().isExpired()) {
                log.info("设备 {} 已有有效Token，返回现有Token", deviceId);
                return existingToken.get().getToken();
            }
        }
        
        // 3. 生成新Token
        String newToken = tokenService.generateAccessToken(userDetails);
        
        log.info("用户 {} 多设备登录成功，当前设备数: {}", 
                userDetails.getUsername(), tokenService.getOnlineDeviceCount(userId));
        return newToken;
    }

    /**
     * 同用户返回同一Token处理
     * 如果用户已有有效Token，返回现有Token；否则生成新Token
     *
     * @param userDetails 用户详情
     * @return Token字符串
     */
    private String handleReuseToken(MaskUserDetails userDetails) {
        Long userId = userDetails.getUserId();
        
        log.info("同用户返回同一Token模式 - 用户: {}", userDetails.getUsername());
        
        // 1. 查找用户的有效Token
        List<TokenInfo> userTokens = tokenService.getUserAllTokens(userId);
        Optional<TokenInfo> validToken = userTokens.stream()
                .filter(token -> !token.isExpired())
                .findFirst();
        
        if (validToken.isPresent()) {
            TokenInfo existingToken = validToken.get();
            log.info("用户 {} 已有有效Token，返回现有Token: {}", 
                    userDetails.getUsername(), existingToken.getToken());
            
            // 更新最后活跃时间
            existingToken.updateLastActiveTime();
            return existingToken.getToken();
        }
        
        // 2. 没有有效Token，生成新Token
        String newToken = tokenService.generateAccessToken(userDetails);
        
        log.info("用户 {} 首次登录，生成新Token", userDetails.getUsername());
        return newToken;
    }

    /**
     * 默认登录处理
     * 根据配置决定使用哪种策略
     *
     * @param userDetails 用户详情
     * @return Token字符串
     */
    private String handleDefaultLogin(MaskUserDetails userDetails) {
        // 根据配置决定策略
        if (properties.isKickOutOtherDevices()) {
            return handleSingleSignOn(userDetails);
        } else if (properties.isMultiDeviceLogin()) {
            return handleMultiDeviceLogin(userDetails);
        } else {
            return handleReuseToken(userDetails);
        }
    }

    /**
     * 踢出用户所有Token
     *
     * @param userId 用户ID
     * @param reason 踢出原因
     */
    private void kickOutUserAllTokens(Long userId, String reason) {
        try {
            List<TokenInfo> userTokens = tokenService.getUserAllTokens(userId);
            for (TokenInfo tokenInfo : userTokens) {
                tokenService.logout(tokenInfo.getToken());
                log.debug("踢出Token: {}, 原因: {}", tokenInfo.getToken(), reason);
            }
        } catch (Exception e) {
            log.error("踢出用户所有Token失败", e);
        }
    }

    /**
     * 踢出最旧的设备
     *
     * @param userId 用户ID
     */
    private void kickOutOldestDevice(Long userId) {
        try {
            List<TokenInfo> userTokens = tokenService.getUserAllTokens(userId);
            
            // 找到最旧的Token（按登录时间排序）
            Optional<TokenInfo> oldestToken = userTokens.stream()
                    .filter(token -> !token.isExpired())
                    .min((t1, t2) -> t1.getLoginTime().compareTo(t2.getLoginTime()));
            
            if (oldestToken.isPresent()) {
                TokenInfo tokenToKick = oldestToken.get();
                tokenService.logout(tokenToKick.getToken());
                log.info("踢出最旧设备Token: {}, 设备: {}", 
                        tokenToKick.getToken(), tokenToKick.getDeviceId());
            }
        } catch (Exception e) {
            log.error("踢出最旧设备失败", e);
        }
    }

    /**
     * 根据用户和设备查找Token
     *
     * @param userId 用户ID
     * @param deviceId 设备ID
     * @return Token信息
     */
    private Optional<TokenInfo> findTokenByUserAndDevice(Long userId, String deviceId) {
        try {
            List<TokenInfo> userTokens = tokenService.getUserAllTokens(userId);
            return userTokens.stream()
                    .filter(token -> deviceId != null && deviceId.equals(token.getDeviceId()))
                    .filter(token -> !token.isExpired())
                    .findFirst();
        } catch (Exception e) {
            log.error("查找用户设备Token失败", e);
            return Optional.empty();
        }
    }

    /**
     * 获取用户登录统计信息
     *
     * @param userId 用户ID
     * @return 登录统计信息
     */
    public LoginStatistics getUserLoginStatistics(Long userId) {
        try {
            List<TokenInfo> userTokens = tokenService.getUserAllTokens(userId);
            
            LoginStatistics stats = new LoginStatistics();
            stats.setUserId(userId);
            stats.setTotalDevices(userTokens.size());
            stats.setActiveDevices((int) userTokens.stream().filter(token -> !token.isExpired()).count());
            stats.setLastLoginTime(userTokens.stream()
                    .map(TokenInfo::getLoginTime)
                    .max(Instant::compareTo)
                    .orElse(null));
            
            return stats;
        } catch (Exception e) {
            log.error("获取用户登录统计失败", e);
            return new LoginStatistics();
        }
    }

    /**
     * 登录统计信息
     */
    public static class LoginStatistics {
        private Long userId;
        private int totalDevices;
        private int activeDevices;
        private Instant lastLoginTime;

        // Getters and Setters
        public Long getUserId() { return userId; }
        public void setUserId(Long userId) { this.userId = userId; }

        public int getTotalDevices() { return totalDevices; }
        public void setTotalDevices(int totalDevices) { this.totalDevices = totalDevices; }

        public int getActiveDevices() { return activeDevices; }
        public void setActiveDevices(int activeDevices) { this.activeDevices = activeDevices; }

        public Instant getLastLoginTime() { return lastLoginTime; }
        public void setLastLoginTime(Instant lastLoginTime) { this.lastLoginTime = lastLoginTime; }
    }
}
